package com.uuah.server.transport.provider.mina;

import java.util.List;

import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.exception.UuahException;
import com.uuah.server.UuahServer;
import com.uuah.server.api.IUuahMessage;
import com.uuah.server.api.component.service.IService;
import com.uuah.server.api.endpoint.IUuahEndpoint;
import com.uuah.server.api.transport.IConnector;
import com.uuah.server.api.transport.IMessageAdapter;
import com.uuah.server.config.Constants;
import com.uuah.server.exception.AbstractUuahServerException;
import com.uuah.server.exception.MessagingException;
import com.uuah.server.exception.transport.provider.mina.MinaException;
import com.uuah.server.exception.transport.provider.mina.MinaSessionException;
import com.uuah.server.message.UuahMessage;
import com.uuah.server.transport.AbstractConnector;
import com.uuah.server.transport.provider.mina.events.MinaEvent;
import com.uuah.server.transport.provider.mina.events.SessionClosedEvent;
import com.uuah.server.transport.provider.mina.events.SessionOpenedEvent;
import com.uuah.utils.MapUtils;

/**
 *
 * TODO comment ...
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午02:47:00
 * @version 1.0.0
 */
public class MinaServerHandler implements IoHandler {
	protected transient final Logger logger = LoggerFactory
			.getLogger(getClass());
	protected final AbstractConnector connector;
	protected final IUuahEndpoint endpoint;
	protected final IService service;
	protected final MinaMessageReceiver receiver;
	protected int readerIdle;
	protected int writerIdle;
	protected int bothIdle;
	protected boolean sendOpenCloseEvents;
	protected boolean sendIdleEvents;
	protected boolean waitForSynchronousResponse;

	public MinaServerHandler(IConnector connector, IService service,
			IUuahEndpoint endpoint) {
		super();
		AbstractConnector abstractConnector = (AbstractConnector) connector;
		this.connector = abstractConnector;
		this.service = service;
		this.endpoint = endpoint;
		this.receiver = (MinaMessageReceiver) abstractConnector.getReceiver(
				service, endpoint);

		MinaConnector minaConnector = (MinaConnector) connector;
		readerIdle = MapUtils.getIntValue(endpoint.getProperties(),
				MinaProperties.READER_IDLE_TIMEOUT_PROPERTY, minaConnector
						.getReaderIdleTimeOut());
		writerIdle = MapUtils.getIntValue(endpoint.getProperties(),
				MinaProperties.WRITER_IDLE_TIMEOUT_PROPERTY, minaConnector
						.getWriterIdleTimeOut());
		bothIdle = MapUtils.getIntValue(endpoint.getProperties(),
				MinaProperties.BOTH_IDLE_TIMEOUT_PROPERTY, minaConnector
						.getBothIdleTimeOut());

		sendOpenCloseEvents = MapUtils.getBooleanValue(
				endpoint.getProperties(),
				MinaProperties.SEND_OPEN_CLOSE_EVENTS_PROPERY, minaConnector
						.isSendOpenCloseEvents());

		sendIdleEvents = MapUtils.getBooleanValue(endpoint.getProperties(),
				MinaProperties.SEND_IDLE_EVENTS_PROPERY, minaConnector
						.isSendIdleEvents());

		waitForSynchronousResponse = MapUtils.getBooleanValue(endpoint
				.getProperties(),
				MinaProperties.WAIT_FOR_SYNCHRONOUS_RESPONSE_PROPERTY,
				minaConnector.isWaitForSynchronousResponse());

	}

	protected UuahMessage createMessageWithId(IoSession session, Object message)
			throws MessagingException {
		IMessageAdapter adapter = connector.getMessageAdapter(message);
		return createMessageWithId(session, adapter);
	}

	protected UuahMessage createMessageWithId(IoSession session,
			IMessageAdapter adapter) throws MessagingException {
		UuahMessage uuahMsg = new UuahMessage(adapter);
		Number id = (Number) session
				.getAttribute(MinaProperties.MINA_SESSION_ID_PROPERTY);
		uuahMsg.setLongProperty(MinaProperties.MINA_SESSION_ID_PROPERTY, id
				.longValue());
		if (session.getRemoteAddress() != null) {
			uuahMsg.setProperty(Constants.UUAH_REMOTE_CLIENT_ADDRESS, session
					.getRemoteAddress());
		}
		uuahMsg.setProperty(Constants.UUAH_REMOTE_CLIENT_APP, session
				.getAttribute(MinaProperties.MINA_SESSION_APP_PROPERTY));
		uuahMsg.setProperty(Constants.UUAH_REMOTE_CLIENT_ROLE, session
				.getAttribute(MinaProperties.MINA_SESSION_ROLE_PROPERTY));
		return uuahMsg;
	}

	public void messageSent(IoSession session, Object message) throws Exception {
		UuahServer.getServerStats().incSentEvent();
	}

	public void sessionCreated(IoSession session) throws Exception {
		Number id = ((MinaConnector) connector).addSession(endpoint
				.getEndpointURI().getUri(), session);
		session.setAttribute(MinaProperties.MINA_SESSION_ID_PROPERTY, id);
	}

	public void sessionIdle(IoSession session, IdleStatus status) {
		if (logger.isTraceEnabled()) {
			Number id = (Number) session
					.getAttribute(MinaProperties.MINA_SESSION_ID_PROPERTY);
			logger.trace("Disconnectiong the idle session: " + id);
		}
		session.close();
	}

	public void sessionClosed(IoSession session) throws Exception {
		Number id = (Number) session
				.getAttribute(MinaProperties.MINA_SESSION_ID_PROPERTY);
		((MinaConnector) connector).removeSession(endpoint.getEndpointURI()
				.getUri(), id);

		if (sendOpenCloseEvents) {
			this.sendSessionClosedEvent(session);
		}
		// try {
		// connector.fireNotification(new AgentMBeanNotification(
		// new IoSessionMBean(session), new ObjectName(
		// "com.uuah.server.transport.mina:type=session,name=session_"
		// + ((InetSocketAddress) session
		// .getRemoteAddress()).getAddress()
		// .getHostAddress()),
		// AgentMBeanNotification.DELETE_MBEAN_ACTION));
		// } catch (Exception e) {
		// logger.error("{}", e);
		// }
	}

	public void sessionOpened(IoSession session) throws Exception {
		if (sendOpenCloseEvents) {
			this.sendSessionOpenedEvent(session);
		}
		// try {
		// connector.fireNotification(new AgentMBeanNotification(
		// new IoSessionMBean(session), new ObjectName(
		// "com.uuah.server.transport.mina:type=session,name=session_"
		// + ((InetSocketAddress) session
		// .getRemoteAddress()).getAddress()
		// .getHostAddress()),
		// AgentMBeanNotification.CREATE_MBEAN_ACTION));
		// } catch (Exception e) {
		// logger.error("{}", e);
		// }
	}

	public void exceptionCaught(IoSession session, Throwable cause) {
		Number id = (Number) session
				.getAttribute(MinaProperties.MINA_SESSION_ID_PROPERTY);
		receiver.handleException(new MinaSessionException(id, cause));
	}

	public void handleException(Exception e) {
		receiver.handleException(new MinaException(e));
	}

	protected void sendSessionClosedEvent(IoSession session)
			throws MessagingException, UuahException {
		Number id = (Number) session
				.getAttribute(MinaProperties.MINA_SESSION_ID_PROPERTY);
		MinaEvent evt = new SessionClosedEvent(id);
		IUuahMessage uuahMsg = this.createMessageWithId(session, evt);

		this.routeMessage(uuahMsg, session);

		if (logger.isTraceEnabled()) {
			logger.trace("Session " + id + " closed");
		}
	}

	protected void sendSessionOpenedEvent(IoSession session)
			throws MessagingException, UuahException {
		Number id = (Number) session
				.getAttribute(MinaProperties.MINA_SESSION_ID_PROPERTY);
		MinaEvent evt = new SessionOpenedEvent(id);
		UuahMessage uuahMsg = this.createMessageWithId(session, evt);
		this.routeMessage(uuahMsg, session);

		if (logger.isTraceEnabled()) {
			logger.trace("Session " + id + " opened");
		}

	}

	protected void routeMessage(IUuahMessage msg, IoSession session)
			throws UuahException {
		IUuahMessage result = null;
		try {
			result = receiver.routeMessage(msg, this.endpoint.isSynchronous());
		} catch (AbstractUuahServerException e) {
			logger.error(e.getMessage());
		}
		if (null != result) {
			if (logger.isDebugEnabled()) {
				logger.debug("Message returned synchronously "
						+ "(Endpoint synch: " + this.endpoint.isSynchronous()
						+ "): " + result);

			}
			boolean ignoreMessage = result.getBooleanProperty(
					MinaProperties.IGNORE_SYNCH_RESPONSE_PROPERTY, false);
			if (waitForSynchronousResponse && !ignoreMessage) {
				Object response = result.getPayload();
				if (response != null) {
					if (response instanceof List) {
						if (((List) response).size() > 0) {
							session.write(response);
						}
					} else {
						session.write(response);
					}
				}
			} else {
				if (logger.isDebugEnabled()) {
					logger.trace("Synchronous response in endpoint "
							+ this.endpoint.getName() + " ignored. " + result);
				}
			}

		}

	}

	public void messageReceived(IoSession session, Object message)
			throws UuahException {
		UuahMessage uuahMsg = null;
		try {
			uuahMsg = this.createMessageWithId(session, message);
		} catch (MessagingException e) {
			logger.error(e.getMessage());
		}
		UuahServer.getServerStats().incReceivedEvent();
		this.routeMessage(uuahMsg, session);
	}
}
